"""
ResourceQuota Manager

Manages ResourceQuota resources for WebService CRD based on computeUnit configuration.
"""

import logging
from typing import Dict, Any
from kubernetes import client
from kubernetes.client.rest import ApiException

from common import consts


class ResourceQuotaManagerError(Exception):
    """Exception raised for ResourceQuota management errors"""
    pass


class ResourceQuotaManager:
    """Manager for ResourceQuota resources"""

    def __init__(self):
        """Initialize the ResourceQuota manager"""
        self.logger = logging.getLogger(__name__)
        self.v1 = client.CoreV1Api()

    def create_resource_quota(self, webservice_spec, webservice_name: str, namespace: str, webservice=None) -> None:
        """
        Create ResourceQuota based on computeUnit configuration
        
        Args:
            webservice_spec: WebService spec containing quota configuration
            webservice_name: Name of the WebService
            namespace: Target namespace for the ResourceQuota
            webservice: WebService object for event sending (optional)
            
        Raises:
            ResourceQuotaManagerError: If creation fails
        """
        try:
            # Get computeUnit from spec
            compute_unit = webservice_spec.quota.compute_unit if hasattr(webservice_spec, 'quota') and webservice_spec.quota else 1
            self.logger.info(f"Creating ResourceQuota for WebService {webservice_name} with computeUnit: {compute_unit}")
            
            # Calculate resource quotas based on computeUnit
            quota_spec = self._calculate_quota_spec(compute_unit)
            self.logger.debug(f"Calculated quota spec: {quota_spec}")
            
            # Create ResourceQuota object
            resource_quota = client.V1ResourceQuota(
                metadata=client.V1ObjectMeta(
                    name=f"{webservice_name}-quota",
                    namespace=namespace,
                    labels={
                        "managed-by": consts.APPLICATION_NAME
                    }
                ),
                spec=client.V1ResourceQuotaSpec(
                    hard=quota_spec
                )
            )
            
            # Create the ResourceQuota
            self.v1.create_namespaced_resource_quota(
                namespace=namespace,
                body=resource_quota
            )
            self.logger.info(f"Successfully created ResourceQuota {webservice_name}-quota in namespace {namespace}")
            
            # Send event for quota creation
            if webservice:
                webservice.info(action='CreateResourceQuota', reason='QuotaCreated',
                              msg=f'ResourceQuota created for WebService {webservice_name}. Limits: {quota_spec}')
            else:
                self.info(action='CreateResourceQuota', reason='QuotaCreated',
                        msg=f'ResourceQuota created for WebService {webservice_name}. Limits: {quota_spec}',
                        webservice_name=webservice_name, namespace=namespace)
            
        except ApiException as e:
            if e.status == 409:  # Already exists
                self.logger.info(f"ResourceQuota {webservice_name}-quota already exists, updating...")
                # Update existing ResourceQuota
                self.update_resource_quota(webservice_spec, webservice_name, namespace, webservice)
            else:
                self.logger.error(f"Failed to create ResourceQuota: {e}")
                raise ResourceQuotaManagerError(f"Failed to create ResourceQuota: {e}")
        except Exception as e:
            self.logger.error(f"Failed to create ResourceQuota: {e}")
            # Send error event if webservice is available
            if webservice:
                webservice.error(action='CreateResourceQuota', reason='QuotaCreationFailed',
                               msg=f'Failed to create ResourceQuota for WebService {webservice_name}: {e}')
            raise ResourceQuotaManagerError(f"Failed to create ResourceQuota: {e}")

    def update_resource_quota(self, webservice_spec, webservice_name: str, namespace: str, webservice=None) -> None:
        """
        Update existing ResourceQuota based on computeUnit configuration
        
        Args:
            webservice_spec: WebService spec containing quota configuration
            webservice_name: Name of the WebService
            namespace: Target namespace for the ResourceQuota
            webservice: WebService object for event sending (optional)
            
        Raises:
            ResourceQuotaManagerError: If update fails
        """
        try:
            # Get computeUnit from spec
            compute_unit = webservice_spec.quota.compute_unit if hasattr(webservice_spec, 'quota') and webservice_spec.quota else 1
            self.logger.info(f"Updating ResourceQuota for WebService {webservice_name} with computeUnit: {compute_unit}")
            
            # Calculate resource quotas based on computeUnit
            quota_spec = self._calculate_quota_spec(compute_unit)
            
            # Get existing ResourceQuota
            try:
                existing_quota = self.v1.read_namespaced_resource_quota(
                    name=f"{webservice_name}-quota",
                    namespace=namespace
                )
                
                # Check if quota values have changed
                if self._quota_values_changed(existing_quota.spec.hard, quota_spec):
                    # Update the ResourceQuota
                    existing_quota.spec.hard = quota_spec
                    self.v1.patch_namespaced_resource_quota(
                        name=f"{webservice_name}-quota",
                        namespace=namespace,
                        body=existing_quota
                    )
                    self.logger.info(f"Successfully updated ResourceQuota {webservice_name}-quota in namespace {namespace}")
                    
                    # Send event for quota update
                    if webservice:
                        webservice.info(action='UpdateResourceQuota', reason='QuotaUpdated',
                                      msg=f'ResourceQuota updated for WebService {webservice_name}. New limits: {quota_spec}')
                else:
                    self.logger.debug(f"ResourceQuota {webservice_name}-quota values unchanged, skipping update")
                
            except ApiException as e:
                if e.status == 404:  # Not found
                    # Create new ResourceQuota
                    self.create_resource_quota(webservice_spec, webservice_name, namespace, webservice)
                else:
                    raise ResourceQuotaManagerError(f"Failed to update ResourceQuota: {e}")
        except Exception as e:
            self.logger.error(f"Failed to update ResourceQuota: {e}")
            # Send error event if webservice is available
            if webservice:
                webservice.error(action='UpdateResourceQuota', reason='QuotaUpdateFailed',
                               msg=f'Failed to update ResourceQuota for WebService {webservice_name}: {e}')
            raise ResourceQuotaManagerError(f"Failed to update ResourceQuota: {e}")

    def delete_resource_quota(self, webservice_name: str, namespace: str, webservice=None) -> None:
        """
        Delete ResourceQuota
        
        Args:
            webservice_name: Name of the WebService
            namespace: Target namespace for the ResourceQuota
            webservice: WebService object for event sending (optional)
            
        Raises:
            ResourceQuotaManagerError: If deletion fails
        """
        try:
            self.v1.delete_namespaced_resource_quota(
                name=f"{webservice_name}-quota",
                namespace=namespace
            )
            self.logger.info(f"Successfully deleted ResourceQuota {webservice_name}-quota from namespace {namespace}")
            
            # Send event for quota deletion
            if webservice:
                webservice.info(action='DeleteResourceQuota', reason='QuotaDeleted',
                              msg=f'ResourceQuota deleted for WebService {webservice_name}')
        except ApiException as e:
            if e.status == 404:  # Not found
                self.logger.info(f"ResourceQuota {webservice_name}-quota not found, already deleted")
            else:
                self.logger.error(f"Failed to delete ResourceQuota: {e}")
                raise ResourceQuotaManagerError(f"Failed to delete ResourceQuota: {e}")
        except Exception as e:
            self.logger.error(f"Failed to delete ResourceQuota: {e}")
            raise ResourceQuotaManagerError(f"Failed to delete ResourceQuota: {e}")

    def _calculate_quota_spec(self, compute_unit: int) -> Dict[str, str]:
        """
        Calculate ResourceQuota spec based on computeUnit
        
        Args:
            compute_unit: Number of compute units
            
        Returns:
            Dictionary containing resource quota specifications
        """
        # 1个计算单元 = limit CPU 1000m, limit RAM 1Gi, request = limit/2
        cpu_limit = f"{compute_unit * 1000}m"
        memory_limit = f"{compute_unit * 1}Gi"
        cpu_request = f"{compute_unit * 500}m"  # limit/2
        memory_request = f"{compute_unit * 0.5}Gi"  # limit/2
        
        return {
            "requests.cpu": cpu_request,
            "requests.memory": memory_request,
            "limits.cpu": cpu_limit,
            "limits.memory": memory_limit
        }

    def _quota_values_changed(self, existing_hard: Dict[str, str], new_quota_spec: Dict[str, str]) -> bool:
        """
        Check if quota values have changed
        
        Args:
            existing_hard: Existing quota hard limits
            new_quota_spec: New quota specification
            
        Returns:
            True if values have changed, False otherwise
        """
        # Convert existing_hard to dict if it's a V1ResourceList object
        if hasattr(existing_hard, '__dict__'):
            existing_dict = {}
            for key, value in existing_hard.__dict__.items():
                if value is not None:
                    existing_dict[key] = str(value)
        else:
            existing_dict = existing_hard or {}
        
        # Compare the values
        for key, new_value in new_quota_spec.items():
            existing_value = existing_dict.get(key)
            if existing_value != new_value:
                self.logger.debug(f"Quota value changed for {key}: {existing_value} -> {new_value}")
                return True
        
        return False
